package api

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/go-resty/resty/v2"
)

type ClientConfig struct {
	Scheme string // http/https
	Host   string // host
	Port   int

	Username string
	Password string
}

type API struct {
	c ClientConfig

	token string
	cli   *resty.Client
}

func NewAPI(c ClientConfig) *API {
	return &API{
		c:   c,
		cli: resty.New(),
	}
}

func (tis *API) GetToken() (string, error) {
	if len(tis.token) > 0 {
		return tis.token, nil
	}

	if err := tis.Login(); err != nil {
		return "", err
	}

	if len(tis.token) > 0 {
		return tis.token, nil
	}

	return "", fmt.Errorf("empty token")
}

func (tis *API) Login() error {
	c := tis.c
	cli := tis.cli

	var uri = fmt.Sprintf("%v://%v:%v/bvcsp/v1/auth/login", c.Scheme, c.Host, c.Port)
	var login = JsonLogin{
		Username:          c.Username,
		PasswordEncrypted: "",
		Timestamp:         time.Now().Unix(),
	}

	login.PasswordEncrypted = login.getEncrypted(c.Password)

	reply, err := cli.R().SetBody(login).Post(uri)
	if err != nil {
		log.Println(err)
		return err
	}

	var objectReply JsonLoginReply
	if err = json.Unmarshal(reply.Body(), &objectReply); err != nil {
		log.Println(err)
		return err
	}

	if objectReply.Code != 0 {
		log.Println(objectReply.Msg)
		return fmt.Errorf("login failed: %v", objectReply.Msg)
	}

	if objectReply.Data.Token == "" {
		log.Println("login failed: empty token")
		return fmt.Errorf("login failed: empty token")

	}

	tis.token = objectReply.Data.Token

	return nil
}

type JsonOpenDialogReply struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data struct {
		Dialogid string `json:"dialogid"`
		Sdp      string `json:"sdp"`
	} `json:"data"`
}

func (tis *API) OpenDialog(puId string, channel int, offerSDP []byte) (string, []byte, error) {
	token, err := tis.GetToken()
	if err != nil {
		return "", nil, err
	}

	cli := tis.cli
	uri := fmt.Sprintf("%v://%v:%v/bvcsp/v1/dialog/device/bvrtc", tis.c.Scheme, tis.c.Host, tis.c.Port)

	reply, err := cli.R().SetHeader("Authorization", token).SetBody(map[string]any{
		"id":    puId,
		"index": channel,
		"sdp":   string(offerSDP),
	}).Post(uri)
	if err != nil {
		log.Println(err)
		return "", nil, err
	}

	var object JsonOpenDialogReply
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		log.Println(err)
		return "", nil, err
	}

	if object.Code != 0 {
		log.Println(object.Msg)
		return "", nil, fmt.Errorf(object.Msg)
	}

	return object.Data.Dialogid, []byte(object.Data.Sdp), nil
}

// ------------------------------------------------------------------------------------------------

type JsonLoginReply struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data struct {
		Token          string `json:"token"`
		Timeout        int    `json:"timeout"`
		AppID          int    `json:"appID"`
		Remaining      int    `json:"remaining"`
		Locking        int    `json:"locking"`
		ChangePassword bool   `json:"change_password"`
	} `json:"data"`
}

type JsonLogin struct {
	Username          string `json:"username"`
	PasswordEncrypted string `json:"password_encrypted"`
	Timestamp         int64  `json:"timestamp"`
}

func (tis *JsonLogin) getEncrypted(password string) string {
	h := sha256.New()
	h.Write([]byte(fmt.Sprintf("%v_%v", tis.Username, password)))
	hashValue := h.Sum(nil)

	h2 := sha256.New()
	h2.Write([]byte(fmt.Sprintf("%v_%v", hex.EncodeToString(hashValue), tis.Timestamp)))
	hashValue = h2.Sum(nil)

	return "SHA256_T:" + hex.EncodeToString(hashValue)
}
